<!--
 * @author luguoxiang
 * @date 2022/9/17
 * 开源项目：https://gitee.com/lgx1992/lg-soar 求star！请给我star！请帮我点个star！
-->
<template>
  <div v-for="node in data" :key="node.value" class="lg-tree-item">
    <span style="display: none">{{ count }}</span>
    <div class="lg-tree-title" :class="{ 'lg-tree-expanded': expanded.includes(node.value) }">
      <span class="lg-tree-icon" :class="{ 'lg-tree-not-child': !node.children?.length }" @click="expand(node)">
        <slot name="icon" :node="node" :expanded="expanded.includes(node.value)">
          <chevron-right-icon />
          <chevron-down-icon />
        </slot>
      </span>
      <slot name="title" :node="node">
        <t-checkbox v-bind="calc(node)" @change="change(node, $event)">
          <slot name="label" :node="node">{{ node.label }}</slot>
        </t-checkbox>
      </slot>
    </div>
    <slot v-if="expanded.includes(node.value)" name="children" :node="node">
      <div class="lg-tree-children">
        <lg-tree :model-value="modelValue" :expanded="expanded" :data="node.children" @change="emits('change', $event)">
          <template v-for="(x, key) in $slots" #[key]="scope">
            <slot :name="key" v-bind="scope"></slot>
          </template>
        </lg-tree>
      </div>
    </slot>
  </div>
</template>

<script setup lang="ts">
import { ref } from 'vue';
import { ChevronRightIcon, ChevronDownIcon } from 'tdesign-icons-vue-next';
import LgTree from './LgTree.vue';

const emits = defineEmits(['change']);
const props = defineProps({
  data: {
    type: Array,
    default: null,
  },
  modelValue: {
    type: Array,
    default() {
      return [];
    },
  },
  expanded: {
    type: Array,
    default() {
      return [];
    },
  },
});
const count = ref(0);

function expand(node: { value: unknown }) {
  const { expanded } = props;
  const index = expanded.indexOf(node.value);
  if (index === -1) {
    expanded.push(node.value);
  } else {
    expanded.splice(index, 1);
  }
  count.value++;
}

/**
 * 处理节点选中状态
 * @param node 节点
 * @param flag 是否选中
 */
function checked(node: any, flag: boolean) {
  const { modelValue } = props;
  if (node.children?.length) {
    // 非叶子节点去除选中值
    const index = modelValue.indexOf(node.value);
    if (index !== -1) {
      modelValue.splice(index, 1);
    }
    // 处理子节点
    node.children.forEach((x: any) => checked(x, flag));
    return;
  }
  // 处理叶子节点
  const index = modelValue.indexOf(node.value);
  if (index === -1) {
    if (flag) {
      modelValue.push(node.value);
    }
  } else if (!flag) {
    modelValue.splice(index, 1);
  }
}

/**
 * 节点选中状态变化
 * @param node
 * @param flag
 */
function change(node: any, flag: boolean) {
  checked(node, flag);
  emits('change', props.modelValue);
}

/**
 * 判断是否所有叶子节点选中状态
 * true：所有叶子节点选中
 * undefined：部分叶子节点选中
 * false：所有叶子节点未选中
 * @param list
 */
function allLeaf(list: any) {
  const { modelValue } = props;
  let count = 0;
  for (const x of list) {
    if (x.children?.length) {
      // 判断是否所有
      const b = allLeaf(x.children);
      if (b === undefined) {
        return undefined;
      }
      if (b === true) {
        count++;
      }
    } else if (modelValue.includes(x.value)) {
      count++;
    }
  }
  if (list.length === count) {
    return true;
  }
  return 0 === count ? false : undefined;
}

/**
 * 计算节点状态
 * @param node
 */
function calc(node: { children: string | any[]; value: unknown }) {
  if (node.children?.length) {
    // 非叶子节点
    const b = allLeaf(node.children);
    if (b === undefined) {
      return { indeterminate: true };
    }
    return { checked: b };
  }
  // 叶子节点
  return { checked: props.modelValue.includes(node.value) };
}
</script>

<style lang="less" scoped>
@border: 1px solid var(--td-component-border);
@indent: var(--lg-tree-indent, 1em);
.lg-tree-item {
  position: relative;
  > .lg-tree-children {
    padding-left: @indent;
    position: relative;
    /* 竖线 */
    &:before {
      border-left: @border;
      content: '';
      height: calc(100% - 1em);
      width: 0;
      position: absolute;
      left: 0.5em;
      bottom: calc(var(--app-height-component) / 2);
    }
    > .lg-tree-item {
      /* 横线 */
      &:after {
        border-bottom: @border;
        content: '';
        height: 0;
        width: @indent;
        position: absolute;
        left: calc(0.5em - @indent);
        top: calc(var(--app-height-component) / 2);
      }
    }
  }

  > .lg-tree-title {
    display: flex;
    height: var(--app-height-component);
    line-height: var(--app-height-component);
    .lg-tree-icon {
      cursor: pointer;
      display: inline-block;
      width: 2em;
      padding: 0 0.5em;
    }
    .lg-tree-icon.lg-tree-not-child {
      cursor: text;
      > * {
        display: none;
      }
    }
  }

  > .lg-tree-expanded {
    .t-icon-chevron-right {
      display: none;
    }
    .t-icon-chevron-down {
      display: unset;
    }
  }
  .t-icon-chevron-down {
    display: none;
  }
}
</style>
